home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / smaltalk / manchest.lha / MANCHESTER / usenet / st80_pre4 / montana.st < prev    next >
Text File  |  1993-07-24  |  43KB  |  1,588 lines

  1. "    NAME        montana
  2.     AUTHOR        Dr Kevin Waite <kww@cs.glasgow.ac.uk>
  3.     FUNCTION The game of Montana
  4.     ST-VERSIONS    2.5
  5.     PREREQUISITES     
  6.     CONFLICTS    
  7.     DISTRIBUTION      world
  8.     VERSION        1.1
  9.     DATE        29 Nov 90
  10.     SUMMARY    A card game called Montana.
  11. "!
  12. "
  13. From: kww@cs.glasgow.ac.uk (Dr Kevin Waite)
  14. Newsgroups: comp.lang.smalltalk
  15. Subject: Montana Game
  16. Message-ID: <7072@vanuata.cs.glasgow.ac.uk>
  17. Date: 29 Nov 90 09:58:37 GMT
  18. Organization: Computing Sci, Glasgow Univ, Scotland
  19.  
  20. My original posting of an implementation of the Montana game
  21. seems to have got lost.   Since I think it is a reasonable example
  22. of simple MVC use (and a reasonable game to boot) I think some
  23. people might find it useful.   If it doesn't get out this time
  24. then too bad.   I hope you enjoy it.   
  25.  
  26. PS  This version includes some heuristics for automatic playing
  27. of the game.   The best one finishes about 1 in 10 games.
  28.  
  29. Address: Dept. of Computing Science,  University of Glasgow,
  30.      17 Lilybank Gardens,  Glasgow,  United Kingdom.  G12 8QQ
  31. "
  32.  
  33.  
  34. Object subclass: #PlayingCard
  35.     instanceVariableNames: 'value '
  36.     classVariableNames: ''
  37.     poolDictionaries: ''
  38.     category: 'Montana'!
  39. PlayingCard comment:
  40. 'I am an abstract superclass for playing cards.  My subclasses
  41. implement the four suits (Hearts, Spades, Clubs and Diamonds).
  42. Their instances have a value fixed at creation time.  They can
  43. be displayed graphically.'!
  44.  
  45.  
  46. !PlayingCard methodsFor: 'accessing'!
  47.  
  48. value
  49.     ^value!
  50.  
  51. value: anInteger
  52.     value := anInteger.! !
  53.  
  54. !PlayingCard methodsFor: 'converting'!
  55.  
  56. ancestor
  57.     "Return my ancestor card.  This is defined to be the card
  58.     of the same suit but with a face value one lower than the
  59.     receiver."
  60.  
  61.     | newValue ancestor |
  62.  
  63.     self value = 1 ifTrue: [self error: 'Aces do not have ancestors.'].
  64.     newValue := self value - 1.
  65.     ancestor := self class value: newValue.
  66.     ^ancestor!
  67.  
  68. descendent
  69.     "Return my descendent card.  This is defined to be the card
  70.     of the same suit but with a face value one higher than the
  71.     receiver."
  72.  
  73.     | newValue ancestor |
  74.  
  75.     self value = 13 ifTrue: [^nil  "Kings do not have descendents"].
  76.     newValue := self value + 1.
  77.     ancestor := self class value: newValue.
  78.     ^ancestor! !
  79.  
  80. !PlayingCard methodsFor: 'displaying'!
  81.  
  82. displayAt: origin
  83.     self displayOn: Display at: origin!
  84.  
  85. displayOn: aForm at: origin 
  86.     self
  87.         displayOn: aForm
  88.         at: origin
  89.         clippingBox: (0 @ 0 extent: Display extent)!
  90.  
  91. displayOn: aForm at: origin clippingBox: box
  92.     | image number aRect x y |
  93.  
  94.     image := self class image.
  95.     number := self formattedNumber asDisplayText.
  96.     aRect := origin extent: self class extent.
  97.     aForm white: aRect.
  98.     aForm border: aRect width: 2.
  99.     
  100.     x := 3 + ((16 - number width) // 2).
  101.     y := 2 + (aRect height - number height) // 2.
  102.     number displayOn: aForm at: (x@y) + origin clippingBox: box.
  103.  
  104.     x := aRect width - image width - 4.
  105.     y := 1 + (aRect height - image height) // 2.
  106.     image displayOn: aForm at: (x@y) + origin clippingBox: box.! !
  107.  
  108. !PlayingCard methodsFor: 'printing'!
  109.  
  110. formattedNumber
  111.     self value = 1 ifTrue: [^'A'].
  112.     self value <= 10 ifTrue: [^self value printString].
  113.     self value = 11 ifTrue: [^'J'].
  114.     self value = 12 ifTrue: [^'Q'].
  115.     self value = 13 ifTrue: [^'K'].
  116.     ^'Unknown'!
  117.  
  118. printOn: aStream
  119.     aStream nextPutAll: self formattedNumber.
  120.     aStream nextPutAll: ' of '.
  121.     self printSuitOn: aStream.!
  122.  
  123. printSuitOn: aStream
  124.     aStream nextPutAll: 'Unknown'.! !
  125.  
  126. !PlayingCard methodsFor: 'testing'!
  127.  
  128. = otherCard
  129.     "Are the receiver and otherCard the same?"
  130.  
  131.     self class == otherCard class ifFalse: [^false].
  132.     ^self value = otherCard value!
  133.  
  134. isAce
  135.     "Is the receiver an ace?"
  136.  
  137.     ^self value = 1!
  138.  
  139. isKing
  140.     "Is the receiver a King?"
  141.  
  142.     ^self value = 13! !
  143. "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
  144.  
  145. PlayingCard class
  146.     instanceVariableNames: ''!
  147.  
  148.  
  149. !PlayingCard class methodsFor: 'constants'!
  150.  
  151. extent
  152.     "Return the maximum extent of a card's image."
  153.  
  154.     ^44@28! !
  155.  
  156. !PlayingCard class methodsFor: 'instance creation'!
  157.  
  158. ace
  159.     "Return the ace of the receiver class."
  160.  
  161.     ^self value: 1!
  162.  
  163. new
  164.     self error: 'Must use the value: instance creation method.'.!
  165.  
  166. value: aNumber
  167.     ^super new value: ((aNumber max: 1) min: 13)! !
  168.  
  169.  
  170. PlayingCard subclass: #Heart
  171.     instanceVariableNames: ''
  172.     classVariableNames: ''
  173.     poolDictionaries: ''
  174.     category: 'Montana'!
  175. Heart comment:
  176. 'My instances are those playing cards whose suit is Hearts.'!
  177.  
  178.  
  179. !Heart methodsFor: 'printing'!
  180.  
  181. printSuitOn: aStream
  182.     aStream nextPutAll: 'Hearts'.! !
  183. "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
  184.  
  185. Heart class
  186.     instanceVariableNames: ''!
  187.  
  188.  
  189. !Heart class methodsFor: 'constants'!
  190.  
  191. image
  192.     ^Form
  193.         extent: 20 @ 18
  194.         fromArray: #(514 0 1285 0 2698 32768 5461 16384 10922 40960 21845 20480 43690 40960 21845 20480 43690 40960 21845 16384 10922 32768 5461 0 2730 0 1364 0 680 0 336 0 160 0 64 0 )
  195.         offset: 0 @ 0! !
  196.  
  197.  
  198. PlayingCard subclass: #Spade
  199.     instanceVariableNames: ''
  200.     classVariableNames: ''
  201.     poolDictionaries: ''
  202.     category: 'Montana'!
  203. Spade comment:
  204. 'My instances are those playing cards whose suit is Spades.'!
  205.  
  206.  
  207. !Spade methodsFor: 'printing'!
  208.  
  209. printSuitOn: aStream
  210.     aStream nextPutAll: 'Spades'.! !
  211. "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
  212.  
  213. Spade class
  214.     instanceVariableNames: ''!
  215.  
  216.  
  217. !Spade class methodsFor: 'constants'!
  218.  
  219. image
  220.     ^Form
  221.         extent: 14 @ 20
  222.         fromArray: #(768 1920 4032 8160 16368 32760 65532 65532 65532 65532 65532 65532 31992 15600 1920 768 4032 4032 768 768 )
  223.         offset: 0 @ 0! !
  224.  
  225.  
  226. PlayingCard subclass: #Diamond
  227.     instanceVariableNames: ''
  228.     classVariableNames: ''
  229.     poolDictionaries: ''
  230.     category: 'Montana'!
  231. Diamond comment:
  232. 'My instances are those playing cards whose suit is Diamonds.'!
  233.  
  234.  
  235. !Diamond methodsFor: 'printing'!
  236.  
  237. printSuitOn: aStream
  238.     aStream nextPutAll: 'Diamonds'.! !
  239. "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
  240.  
  241. Diamond class
  242.     instanceVariableNames: ''!
  243.  
  244.  
  245. !Diamond class methodsFor: 'constants'!
  246.  
  247. image
  248.     ^Form
  249.     extent: 18@18
  250.     fromArray: #( 128 0 320 0 672 0 1360 0 2728 0 5460 0 10922 0 21845 0 43690 32768 21845 16384 10922 32768 5461 0 2730 0 1364 0 680 0 336 0 160 0 64 0)
  251.     offset: 0@0! !
  252.  
  253.  
  254.  
  255. PlayingCard subclass: #Club
  256.     instanceVariableNames: ''
  257.     classVariableNames: ''
  258.     poolDictionaries: ''
  259.     category: 'Montana'!
  260. Club comment:
  261. 'My instances are those playing cards whose suit is Clubs.'!
  262.  
  263.  
  264. !Club methodsFor: 'printing'!
  265.  
  266. printSuitOn: aStream
  267.     aStream nextPutAll: 'Clubs'.! !
  268. "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
  269.  
  270. Club class
  271.     instanceVariableNames: ''!
  272.  
  273.  
  274. !Club class methodsFor: 'constants'!
  275.  
  276. image
  277.     ^Form
  278.         extent: 14 @ 18
  279.         fromArray: #(768 1920 4032 4032 4032 1920 13104 31608 65532 65532 65532 65532 31992 14448 768 768 4032 4032 )
  280.         offset: 0 @ 0! !
  281.  
  282.  
  283.  
  284. FormView subclass: #MontanaView
  285.     instanceVariableNames: ''
  286.     classVariableNames: ''
  287.     poolDictionaries: ''
  288.     category: 'Montana'!
  289. MontanaView comment:
  290. 'My instances provide a graphical representation of the state of the
  291. board in a game of Montana.  My model is the instance of Montana.
  292. My instances listen for two update messages broadcast by my model.
  293. The first, #game, causes the entire board image to be refreshed.  This
  294. is typically sent when a board is being reshuffled and at the start of a 
  295. game.  The second message, #move, takes an argument describing the
  296. last move made by the player.   This is used to animate the movement
  297. of the card over the board.'!
  298.  
  299.  
  300. !MontanaView methodsFor: 'controller access'!
  301.  
  302. defaultControllerClass
  303.     ^MontanaController! !
  304.  
  305. !MontanaView methodsFor: 'displaying'!
  306.  
  307. animateCardFrom: start to: finish
  308.     "A card has been moved from start position to finish position.
  309.     Show this move by animating the movement of the displayed
  310.     card between these two positions.  This is done by having the
  311.     card follow a linear trajectory between these two points with
  312.     ten equal steps."
  313.  
  314.     | startBox finishBox steps delta locus pause trajectory image count |
  315.  
  316.     startBox := self boundingBoxForPosition: start.
  317.     finishBox := self boundingBoxForPosition: finish.
  318.     steps := 10.
  319.     delta := (finishBox origin - startBox origin) // steps.
  320.     locus := startBox origin.
  321.     pause := Delay forMilliseconds: 50.
  322.     trajectory := [pause wait.  locus := locus + delta].
  323.     image := Form fromDisplay: startBox.
  324.     count := steps.
  325.     
  326.     Display gray: startBox.
  327.     Cursor execute showWhile:
  328.         [image follow: trajectory 
  329.             while: [count := count - 1.  count > 0]].
  330.  
  331.     image displayAt: finishBox origin.!
  332.  
  333. boundingBoxForPosition: position
  334.     "Return the rectangle (expressed in Display coordinates)
  335.      giving the bounding box of the Montana board at the given
  336.     position."
  337.  
  338.     | count cardSize offset x y aRect displacement |
  339.  
  340.     cardSize := PlayingCard extent.
  341.     offset := 5.
  342.  
  343.     count := position y.
  344.     x := (count * offset) + ((count-1) * cardSize x).
  345.  
  346.     count := position x.
  347.     y := (count * offset) + ((count-1) * cardSize y).
  348.  
  349.     displacement := self insetDisplayBox origin.
  350.     aRect := (x@y extent: cardSize) translateBy: displacement.
  351.     ^aRect!
  352.  
  353. displayView 
  354.     "Completely regenerate the display of my model's board state."
  355.  
  356.     | origin cardSize y offset clipBox |
  357.  
  358.     Display fill: self insetDisplayBox mask: Form gray.
  359.     origin := self insetDisplayBox origin.
  360.     cardSize := PlayingCard extent.
  361.     offset := 5.
  362.     y := origin y + offset.
  363.     clipBox := self insetDisplayBox.
  364.  
  365.     self model board do: [:row | | x |
  366.         x := origin x + offset.
  367.         row do: [:card |
  368.             card isNil ifFalse: [
  369.                 card displayOn: Display at: x@y clippingBox: clipBox].
  370.  
  371.             x := x + cardSize x + offset.
  372.         ].
  373.         y := y + cardSize y + offset.
  374.     ].!
  375.  
  376. highlightPosition: position
  377.     "Highlight the board at the given position by turning
  378.     the image at that location using reverse video."
  379.  
  380.     | aRect |
  381.  
  382.     aRect := self boundingBoxForPosition: position.
  383.     Display reverse: aRect.!
  384.  
  385. showAncestorOfCardAt: position
  386.     "This method highlights the ancestor to the card
  387.     at the given position.  (See the comment of class
  388.     PlayingCard for a definition of ancestor).  If there 
  389.     is no such ancestor then flash the display."   
  390.  
  391.     | thisCard ancestor locus |
  392.  
  393.     thisCard := self model cardAt: position.
  394.     (thisCard isNil or: [thisCard value = 2]) 
  395.         ifTrue: [^self flash].
  396.  
  397.     ancestor := thisCard ancestor.
  398.     ancestor isNil ifTrue: [^self flash].
  399.  
  400.     locus := self model positionOfCard: ancestor.
  401.     self highlightPosition: locus.
  402.     Sensor waitNoButton.
  403.     self highlightPosition: locus.!
  404.  
  405. showDescendentOfCardAt: position
  406.     "This method highlights the descendent to the card
  407.     at the given position.  (See the comment of class
  408.     PlayingCard for a definition of descendent).  If there 
  409.     is no such descendent then flash the display."   
  410.  
  411.     | thisCard desc locus |
  412.  
  413.     thisCard := self model cardToLeftOf: position.
  414.     thisCard isNil ifTrue: [^self flash].
  415.     desc := thisCard descendent.
  416.     desc isNil ifTrue: [^self flash].
  417.  
  418.     locus := self model positionOfCard: desc.
  419.     self highlightPosition: locus.
  420.     Sensor waitNoButton.
  421.     self highlightPosition: locus.!
  422.  
  423. update: aspect with: aMove
  424.     "A change has occurred in the state of my model.  
  425.     Depending upon the aspect, update the display accordingly."
  426.  
  427.     (aspect == #move and: [aMove notNil])
  428.         ifTrue: [self animateCardFrom: aMove first 
  429.                                     to: aMove last].
  430.  
  431.     aspect == #game ifTrue: [self display].! !
  432.  
  433. !MontanaView methodsFor: 'window access'!
  434.  
  435. defaultWindow 
  436.     "Return the window for a graphical display of a Montana board."
  437.  
  438.     | cardSize offset width height |
  439.  
  440.     cardSize := PlayingCard extent.
  441.     offset := 5.
  442.  
  443.     width := self model class cardsPerSuit * (cardSize x + offset) + offset.
  444.     height := self model class numberOfSuits * (cardSize y + offset) + offset.
  445.     
  446.     ^(Rectangle origin: 0 @ 0 extent: width@height) expandBy: self borderWidth! !
  447.  
  448. Model subclass: #Montana
  449.     instanceVariableNames: 'board shufflesLeft lastMove moveCounter '
  450.     classVariableNames: 'RND '
  451.     poolDictionaries: ''
  452.     category: 'Montana'!
  453. Montana comment:
  454. 'My instances hold the state of play in the Montana game.  For details 
  455. on how to play the game execute the following expression: 
  456.     (Montana instructions).  
  457.  
  458. This version is based on the Macintosh version implemented by Eric Snider.  
  459. It has been elaborated slightly so as to illustrate some aspects of the 
  460. Smalltalk Model-View-Controller framework.   This version started out in 
  461. life as a sample solution to an under-graduate Smalltalk introductory 
  462. exercise.   The game could usefully be extended by a facility for the 
  463. program to play the game to completion.
  464.  
  465. Instance variables:
  466. ''rows''        a 4-by-13 array of Cards or nil.  Holds the state of the board.
  467.             Board positions are accessed using a Point where the x value
  468.             is the row number; the y value is the column number.
  469.  
  470. ''shufflesLeft''    an integer saying how many shuffles the player has left.
  471.  
  472. ''lastMove''        a two-element array describing the last move made by the 
  473.                 player.  At the start of a game and immediately after a shuffle,
  474.                 this value is undefined.   The first element of the array is the
  475.                 position of the card before the move; the second element is the
  476.                 position after the move.
  477.  
  478. (c)    Dr. Kevin Waite, 1990.
  479.     Computing Science Department
  480.     University of Glasgow
  481.     United Kingdom
  482.     Email: kww@cs.glasgow.ac.uk'!
  483.  
  484.  
  485. !Montana methodsFor: 'accessing'!
  486.  
  487. cardAt: aPoint
  488.     "Return the given card at the specified location on the
  489.     Montana board or nil if there is no card."
  490.  
  491.     ^(self board at: aPoint x) at: aPoint y!
  492.  
  493. cardAt: aPoint put: aCard
  494.     "Place the given card at the specified location on the
  495.     Montana board.  A value of nil for aCard means that
  496.     this location no longer has a card."
  497.  
  498.     (self board at: aPoint x) at: aPoint y put: aCard.!
  499.  
  500. lastMove
  501.     "Return the last move made by the player as a
  502.     two-element array whose first element is the
  503.     starting position and the second is the finishing
  504.     position for the card."
  505.  
  506.     ^lastMove!
  507.  
  508. lastMove: anArrayOfTwoPoints
  509.     "Set the last move made by the player as a
  510.     two-element array whose first element is the
  511.     starting position and the second is the finishing
  512.     position for the card."
  513.  
  514.     lastMove := anArrayOfTwoPoints.!
  515.  
  516. moveCounter
  517.     ^moveCounter!
  518.  
  519. random
  520.     "Return the next random number; a value in 
  521.     the range 0..1."
  522.  
  523.     ^RND next!
  524.  
  525. shufflesLeft
  526.     "How many shuffles does the player have left?"
  527.  
  528.     ^shufflesLeft!
  529.  
  530. shufflesLeft: anInteger
  531.     shufflesLeft := anInteger.! !
  532.  
  533. !Montana methodsFor: 'automatic play'!
  534.  
  535. automaticPlay
  536.     "This method is a stub that calls the actual method that does
  537.     the playing.  This allows alternative methods to be tried quite
  538.     easily."
  539.  
  540.     "self randomlyMoveCards."
  541.     "self repeatedPriorityMove."
  542.     self repeatedRunAndJuggle.!
  543.  
  544. createAGapAt: position
  545.     "Open up a gap at the given position.  This may
  546.     involve moving an arbitrary number of cards.  Return
  547.     a boolean saying whether the gap was actually created."
  548.  
  549.     | run   |
  550.  
  551.     run := self getRunStartingAt: position.
  552.      run isNil ifTrue: [^false].
  553.     run do: [:each | self moveCardAtPosition: each].
  554.  
  555.     ^true!
  556.  
  557. getRunStartingAt: position 
  558.     "A run is defined as an OrderedCollection of card positions 
  559.     that moved in order will leave a gap at the specified 
  560.     position. If there is no such run from this position 
  561.     then return nil. If there is already a gap then the 
  562.     run will be empty (but non-nil). A constraint on a 
  563.     run is that a card can only appear once (to avoid 
  564.     cycles)."
  565.  
  566.     ^self
  567.         getRunStartingAt: position
  568.         building: OrderedCollection new
  569.         fixing: Set new!
  570.  
  571. getRunStartingAt: position building: aRun fixing: fixedPositions
  572.     "See the method getRunStartingAt: for details of what
  573.     is a run.  This method is trying to find a run building onto
  574.     the one passed as parameter.  Those cards mentioned in
  575.     fixedPositions cannot be moved since other cards are depending
  576.     on them being in their current position."
  577.  
  578.     | aCard parent holder target |
  579.  
  580.     (aRun includes: position) ifTrue: [^nil  "Cycle."].
  581.     (fixedPositions includes: position) ifTrue: [^nil  "Need this card here"].
  582.     (self numberOfPositionedCardsInRow: position x) >= position y ifTrue: [
  583.         "Prohibit the movement of a card that is in sequence."
  584.         ^nil.
  585.     ].
  586.  
  587.     aCard := self cardAt: position.
  588.     aCard isNil ifTrue: [^aRun  "We found it."].
  589.  
  590.     parent := aCard ancestor.
  591.     parent isAce ifTrue: [
  592.         "This run is only possible if there is a vacant 
  593.         slot in the leftmost column to take aCard (known
  594.         here to be a '2' or if we can create a slot."
  595.         
  596.         1 to: self class numberOfSuits do: [:r |
  597.             (self isCardAt: r @ 1) ifFalse: [
  598.                 aRun addFirst: position.
  599.                 ^aRun
  600.             ].
  601.         ].
  602.  
  603.         aRun addFirst: position.
  604.         1 to: self class numberOfSuits do: [:r |
  605.             | trial result |
  606.  
  607.             trial := aRun deepCopy.
  608.             result := self 
  609.                         getRunStartingAt: (r@1) 
  610.                         building: trial 
  611.                         fixing: fixedPositions.
  612.  
  613.             result isNil ifFalse: [^result]
  614.         ].
  615.         ^nil   "No luck in moving this '2'."
  616.     ].
  617.  
  618.     holder := self positionOfCard: parent.
  619.     fixedPositions add: holder.
  620.  
  621.     holder y = self class cardsPerSuit ifTrue: [
  622.         "Up against the edge of the board.  
  623.         Since nothing will fit in behind it we 
  624.         cannot have a run."
  625.  
  626.         ^nil
  627.     ].
  628.  
  629.     target := holder + (0@1).   "Look at slot one to the right."
  630.     aRun addFirst: position.
  631.     ^self getRunStartingAt: target building: aRun fixing: fixedPositions!
  632.  
  633. placeOrderedCardAt: position
  634.     "This method tries to replace the card this position
  635.     with the one that should be here given its neighbour.
  636.     This first involves opening up a gap here and then 
  637.     moving in the appropriate card.  If we cannot open 
  638.     a gap then return false otherwise true."
  639.  
  640.     | success locus |
  641.  
  642.     success := self createAGapAt: position.
  643.     success ifFalse: [^false].
  644.     position y = 1 ifTrue: [ | aTwo |
  645.         "In the left most column.  Move in an unplace '2'."
  646.         
  647.         aTwo := self findAnUnplacedTwoForRow: position x.
  648.         aTwo isNil ifTrue: [^false].
  649.         locus := self positionOfCard: aTwo.
  650.     ] ifFalse: [ | neighbour |
  651.         neighbour := self cardToLeftOf: position.
  652.         neighbour isNil ifTrue: [^false].
  653.         locus := self positionOfCard: neighbour descendent.
  654.     ].
  655.  
  656.     self moveCardAt: locus to: position.
  657.     ^true!
  658.  
  659. priorityBlock
  660.     "Return a two-variable block that sorts cards into 
  661.     decreasing order of preferrance for a move."
  662.  
  663.     ^[:aCard :bCard |
  664.         (self priorityOfCard: aCard) > 
  665.         (self priorityOfCard: bCard)]!
  666.  
  667. priorityMove
  668.     | movers priorityMovers start  |
  669.  
  670.     movers := self allCardsThatCanMove.
  671.     movers isEmpty ifTrue: [^false].
  672.     priorityMovers := movers asSortedCollection: self priorityBlock.
  673.     start := self positionOfCard: priorityMovers first.
  674.     self moveCardAtPosition: start.
  675.     ^true!
  676.  
  677. priorityOfCard: aCard
  678.     "Return an integer giving the priority that should be
  679.     assigned to the moving of this card.  The high value
  680.     means that this card should always be moved earlier, 
  681.     a low value means move this later."
  682.  
  683.     | position neighbour dest destNeighbour ordered |
  684.     
  685.     position := self positionOfCard: aCard.
  686.     neighbour := self cardToLeftOf: position.
  687.     dest := self destinationForCardAt: position.
  688.     destNeighbour := self cardToLeftOf: dest.
  689.  
  690.     "Case:  Moving a card to its final position."
  691.     destNeighbour == aCard ancestor ifTrue: [^20].
  692.  
  693.     "Case:     Moving a card that will leave a gap that when
  694.             filled will extend a sequence."
  695.  
  696.     ordered := self numberOfPositionedCardsInRow: position x.
  697.     position y = (ordered + 1) ifTrue: [^30].
  698.  
  699.     "Case: Moving a card that is to the right of a gap."
  700.     neighbour isNil ifTrue: [^10].
  701.     
  702.     "Case:    Moving a card that is to the right of a King."
  703.     neighbour isKing ifTrue: [^0].
  704.  
  705.     "Otherwise case:  Moving a card nearer its final position."
  706.     ^(aCard value - position y) abs + 5!
  707.  
  708. randomlyMoveCards
  709.     "This method tries to play the game by randomly moving
  710.     cards until it cannot move anymore.  It then reshuffles and
  711.     continues until it runs out of moves and shuffles."
  712.  
  713.     | moves |
  714.  
  715.     [
  716.         [moves := self randomlyMoveCardsIntoGaps.
  717.         moves > 0] whileTrue.
  718.         (self numberOfCardsInOrder < self class placeableCards)
  719.         and: [self shufflesLeft > 0]
  720.     ] whileTrue: [self shuffle].!
  721.  
  722. randomlyMoveCardsIntoGaps
  723.     "This method tries to play the game by randomly moving cards
  724.     in the hope that this will eventually converge on the solution."
  725.  
  726.     | gaps moves |
  727.  
  728.     gaps := self positionsOfTheGaps.
  729.     moves := 0.
  730.  
  731.     1 to: gaps size do: [:k |
  732.         | gap parent position aCard |
  733.  
  734.         gap := gaps at: k.
  735.         parent := self cardToLeftOf: gap.
  736.  
  737.         aCard := parent isNil 
  738.                     ifFalse: [parent descendent] 
  739.                     ifTrue: ["Move a '2' into this gap."  
  740.                             gap y = 1 
  741.                                 ifTrue: [self findAnUnplacedTwoForRow: gap x]    
  742.                                 ifFalse: [nil]].
  743.          
  744.         aCard isNil ifFalse: [
  745.             position := self positionOfCard: aCard.
  746.             self moveCardAt: position to: gap.
  747.             gaps at: k put: position.
  748.             moves := moves + 1.
  749.         ].
  750.     ].
  751.     ^moves!
  752.  
  753. repeatedPriorityMove
  754.     [
  755.         [self priorityMove] whileTrue.
  756.         (self numberOfCardsInOrder < self class placeableCards)
  757.         and: [self shufflesLeft > 0]
  758.     ] whileTrue: [self shuffle].!
  759.  
  760. repeatedRunAndJuggle
  761.     [
  762.         [self runAndJuggle] whileTrue.
  763.         (self numberOfCardsInOrder < self class placeableCards)
  764.         and: [self shufflesLeft > 0]
  765.     ] whileTrue: [self shuffle].!
  766.  
  767. runAndJuggle
  768.     "This method tries to fill the rows with the proper cards
  769.     by moving in the proper cards.  It tries this for each row
  770.     in turn.   If it cannot do a move in any row it performs a 
  771.     priority-based move in an attempt to free up some space
  772.     that can be used as part of a run-based move.  This continues
  773.     until no more cards can be moved."
  774.  
  775.     | ordered moved |
  776.  
  777.     ordered := Array new: self class numberOfSuits.
  778.     1 to: self class numberOfSuits do: [:r |
  779.         ordered at: r put: (self numberOfPositionedCardsInRow: r)].
  780.  
  781.     moved := false.
  782.     1 to: self class numberOfSuits do: [:r |
  783.         | attempt |
  784.  
  785.         attempt := (ordered at: r) + 1.
  786.         [attempt < self class cardsPerSuit and: [
  787.          self placeOrderedCardAt: r @ attempt]] whileTrue: [
  788.             attempt := attempt + 1.
  789.             moved := true 
  790.         ].
  791.  
  792.         ordered at: r put: (attempt-1).
  793.     ].
  794.     ^moved or: [self priorityMove]! !
  795.  
  796. !Montana methodsFor: 'initialize-release'!
  797.  
  798. initialize
  799.     "Prepare the receiver for the start of play."
  800.  
  801.     board := Array new: self class numberOfSuits.
  802.     1 to: board size do: [:k | | row |
  803.  
  804.         row := Array new: self class cardsPerSuit.
  805.         board at: k put: row.
  806.     ].!
  807.  
  808. newGame
  809.     "Initialize this instance of the game with a random
  810.     distribution of cards."
  811.  
  812.     |  cards |
  813.  
  814.     cards := OrderedCollection new: self class numberOfCards.
  815.     1 to: self class numberOfCards do: [:k | cards add: (self convertToCard: k)].
  816.     self shuffleCards: cards ordered: #(0 0 0 0).
  817.     self resetShuffleCount.
  818.     self resetMoveCounter.
  819.     self changed: #game.! !
  820.  
  821. !Montana methodsFor: 'moving functions'!
  822.  
  823. allCardsThatCanMove
  824.     "Return a collection of the cards that could move 
  825.     given the current state of the board."
  826.  
  827.     | movers gaps |
  828.  
  829.     movers := Set new.
  830.     gaps := self positionsOfTheGaps.
  831.     gaps do: [:gap |
  832.         | parent aCard |
  833.  
  834.         parent := self cardToLeftOf: gap.
  835.  
  836.         aCard := parent isNil 
  837.                     ifFalse: [parent descendent] 
  838.                     ifTrue: ["Move a '2' into this gap."  
  839.                             gap y = 1 
  840.                                 ifTrue: [self findAnUnplacedTwoForRow: gap x]    
  841.                                 ifFalse: [nil]].
  842.          
  843.         aCard isNil ifFalse: [movers add: aCard].
  844.     ].
  845.     ^movers!
  846.  
  847. cardToLeftOf: position
  848.     "Return the card that appears in the same row as 
  849.     the given position but one column to the left.  If there
  850.     is no card at the new location, return nil."
  851.  
  852.     | row col newCol thisCard |
  853.  
  854.     row := position x.
  855.     col := position y.
  856.     newCol := col - 1.
  857.     newCol = 0 ifTrue: [^nil].
  858.  
  859.     thisCard := self cardAt: row @ newCol.
  860.     ^thisCard!
  861.  
  862. destinationForCardAt: index
  863.     "The card at the given index is about to be moved.  Return
  864.     the index of the position where it should go to.  If there is
  865.     already a card there then return nil otherwise return the
  866.     position.  Note that '2' cards must go the first column:  if
  867.     there is no free slot then return nil.  If the index argument
  868.     corresponds to the first column then return the next free
  869.     position in that column that is free or nil if none."
  870.  
  871.     | thisCard |
  872.  
  873.     thisCard := self cardAt: index.
  874.     thisCard value = 2 ifTrue: [
  875.         | r c |
  876.  
  877.         r := index x.   c := index y.
  878.         c = 1 ifTrue: [ "Move to next free slot in first column."
  879.             [r := r = 4 ifTrue: [1] ifFalse: [r+1].
  880.              r = index x ifTrue: [^nil].
  881.              self isCardAt: r@c] whileTrue.
  882.             ^r @ c
  883.         ] ifFalse: [
  884.             1 to: 4 do: [:s | (self isCardAt: s@1) ifFalse: [^s @ 1]].
  885.             ^nil
  886.         ].
  887.     ] ifFalse: ["Not a '2':  find its ancestor."
  888.         | locus destLocus |
  889.  
  890.         locus := self positionOfCard: thisCard ancestor.
  891.  
  892.         "Are we at the end of the row?"
  893.         locus y = self class cardsPerSuit ifTrue: [^nil].
  894.  
  895.         destLocus := locus x @ (locus y + 1).
  896.         ^(self isCardAt: destLocus) ifTrue: [nil] ifFalse: [destLocus]
  897.     ].
  898.     ^nil!
  899.  
  900. moveCardAt: start to: finish
  901.     "Move the card currently at location 'start' to its new
  902.     location 'finish'.  This leaves a gap at start.   It is assumed
  903.     that initially there is a gap at finish.  Once the board has 
  904.     been updated, announce the change giving details of the 
  905.     move so that any graphical display of the board can be 
  906.     updated appropriately."
  907.  
  908.     |  thisMove |
  909.  
  910.     self simplyMoveCardAt: start to: finish.
  911.     thisMove := Array with: start with: finish.
  912.     self lastMove: thisMove.
  913.     self oneMoreMove.
  914.     self changed: #move with: thisMove.
  915.     self changed: #status.!
  916.  
  917. moveCardAtPosition: position
  918.     | destination |
  919.  
  920.     (self isCardAt: position)
  921.         ifFalse: [^self error: 'There is no card here.'].
  922.  
  923.     destination := self destinationForCardAt: position.
  924.     destination isNil ifTrue: [^self error: 'This is an illegal move.'].
  925.  
  926.     self moveCardAt: position to: destination!
  927.  
  928. numberOfCardsInOrder
  929.     "Return the number of cards that are in the proper order.
  930.     This is used to compute the current score in the game."
  931.  
  932.     ^(1 to: self class numberOfSuits) inject: 0 into: [:total :r |
  933.                 total + (self numberOfPositionedCardsInRow: r)]!
  934.  
  935. numberOfPositionedCardsInRow: r
  936.     "How cards are in their correct position in the 
  937.     given row?  A card N is in its proper position if it
  938.     is a member of the sequence 2, 3,...,N with the 
  939.     sequence starting in the leftmost column of the row."
  940.  
  941.     | suit |
  942.  
  943.     suit := nil.
  944.     1 to: self class cardsPerSuit do: [:c | 
  945.         | v thisCard |
  946.  
  947.         v := c + 1.
  948.         thisCard := self cardAt: r @ c.
  949.         suit isNil 
  950.             ifTrue: [suit := thisCard class] 
  951.             ifFalse: [suit == thisCard class 
  952.                         ifFalse: [^c-1]].
  953.  
  954.         (thisCard isNil or: [
  955.         thisCard value ~= v]) ifTrue: [^c-1]
  956.     ].
  957.     ^self class cardsPerSuit!
  958.  
  959. positionOfCard: aCard
  960.     "Return the position of the given card as an instance
  961.     of Point with the x value denoting the row and the y
  962.     value denoting the column occupied by aCard."
  963.  
  964.     1 to: self class numberOfSuits do: [:r |
  965.         1 to: self class cardsPerSuit do: [:c |
  966.             (self cardAt: r @ c) = aCard ifTrue: [^r @ c]]].
  967.  
  968.     self error: 'Could not find the given card.'.!
  969.  
  970. simplyMoveCardAt: start to: finish
  971.     "Move the card currently at location 'start' to its new
  972.     location 'finish'.  This leaves a gap at start.   It is assumed
  973.     that initially there is a gap at finish."
  974.  
  975.     | thisCard  |
  976.  
  977.     (self isCardAt: finish) ifTrue: [self error].
  978.     thisCard := self cardAt: start.
  979.     self cardAt: start put: nil.
  980.     self cardAt: finish put: thisCard.! !
  981.  
  982. !Montana methodsFor: 'private'!
  983.  
  984. board 
  985.     "Returns the current state of the board."
  986.  
  987.     ^board!
  988.  
  989. board: anArrayOfRows
  990.     "Assigns the argument to be the new state of the board."
  991.  
  992.     board := anArrayOfRows!
  993.  
  994. convertToCard: index
  995.     "The argument 'index' is an integer in the 
  996.     range [1,52].  Convert this number into a 
  997.     unique card instance."
  998.  
  999.     | suit number cardClass aCard ind |
  1000.  
  1001.     ind := index - 1.
  1002.     suit := ind // self class cardsPerSuit.
  1003.     number := (ind - (suit * self class cardsPerSuit)) + 1.
  1004.     cardClass :=     suit = 0 ifTrue: [Club] ifFalse: [
  1005.                     suit = 1 ifTrue: [Heart] ifFalse: [
  1006.                     suit = 2 ifTrue: [Spade] ifFalse: [
  1007.                     suit = 3 ifTrue: [Diamond]]]].
  1008.  
  1009.     aCard := cardClass value: number.
  1010.     ^aCard!
  1011.  
  1012. findAnUnplacedTwoForRow: row
  1013.     "Return a '2' card that would best fit into the given
  1014.     row.  There are various situations were one '2' is better
  1015.     than another."
  1016.  
  1017.     | twos preferred neighbour ordered first |
  1018.  
  1019.     "Is there already a '2' as the first card in this row but 
  1020.     not in the left-most column?  If so then move it so that
  1021.     it is in that column."
  1022.  
  1023.     first := self firstCardInRow: row.
  1024.     first value = 2 ifTrue: [^first].
  1025.  
  1026.     "See if there is already a sequence 3,4,... in place that 
  1027.     needs a start."
  1028.  
  1029.     twos := OrderedCollection new.
  1030.     1 to: self class numberOfSuits do: [:r |
  1031.         ordered := self numberOfPositionedCardsInRow: r.
  1032.         "Don't consider solitary '2's as being immovable."
  1033.         (ordered max: 1) to: self class cardsPerSuit do: [:c |
  1034.             | aCard |
  1035.  
  1036.             aCard := self cardAt: r @ c.
  1037.             (aCard notNil and: [aCard value = 2])
  1038.                 ifTrue: [twos add: aCard]
  1039.         ].
  1040.     ].
  1041.  
  1042.     neighbour := self cardAt: row @ 2.
  1043.     preferred := twos detect: [:card | neighbour = card descendent] 
  1044.                         ifNone: [nil].
  1045.  
  1046.     "If there was no preferred two then return one that
  1047.     is not already in the leftmost column."
  1048.  
  1049.     ^preferred isNil 
  1050.         ifFalse: [preferred]
  1051.         ifTrue: [twos 
  1052.                     detect: [:card | (self positionOfCard: card) y > 2]
  1053.                     ifNone: [twos first]]!
  1054.  
  1055. firstCardInRow: r
  1056.     "Return the left-most card in the given row."
  1057.  
  1058.     1 to: self class cardsPerSuit do: [:c |
  1059.         | aCard |
  1060.  
  1061.         aCard := self cardAt: r @ c.
  1062.         aCard isNil ifFalse: [^aCard].
  1063.     ].
  1064.     ^nil!
  1065.  
  1066. oneLessShuffleLeft
  1067.     "The player has used up one more shuffle 
  1068.     of the unordered cards."
  1069.  
  1070.     shufflesLeft := shufflesLeft - 1.
  1071.     self changed: #status.!
  1072.  
  1073. oneMoreMove
  1074.     moveCounter := moveCounter + 1.!
  1075.  
  1076. positionsOfTheGaps
  1077.     "Return an array giving the positions of the
  1078.     gaps in the board."
  1079.  
  1080.     | gaps |
  1081.  
  1082.     gaps := OrderedCollection new.
  1083.     1 to: self class numberOfSuits do: [:r |
  1084.         1 to: self class cardsPerSuit do: [:c |
  1085.             (self isCardAt: r @ c) ifFalse: [gaps add: r @ c]
  1086.         ].
  1087.     ].
  1088.     ^gaps!
  1089.  
  1090. removeAces
  1091.     "Remove the aces from the board in order to create the
  1092.     gaps necessary for moving the other cards."
  1093.  
  1094.     1 to: self class numberOfSuits do: [:r |
  1095.         1 to: self class cardsPerSuit do: [:c |
  1096.             | aCard |
  1097.  
  1098.             aCard := self cardAt: r @ c.
  1099.             (aCard notNil and: [aCard isAce])
  1100.                 ifTrue: [self cardAt: r @ c put: nil].
  1101.         ].
  1102.     ].!
  1103.  
  1104. resetMoveCounter
  1105.     moveCounter := 0.!
  1106.  
  1107. resetShuffleCount
  1108.     "Reset the number of available shuffles to the starting value."
  1109.  
  1110.     shufflesLeft := 2.
  1111.     self changed: #status.!
  1112.  
  1113. shuffleCards: cards ordered: orderedColumns
  1114.     "This method randomly fills the non-ordered columns
  1115.     of the board with cards picked from 'cards'.  This set
  1116.     is destroyed by this method.   The orderedColumns
  1117.     argument states how many columns are ordered for 
  1118.     each row."
  1119.  
  1120.     1 to: self class numberOfSuits do: [:r |
  1121.         | start |
  1122.  
  1123.         start := (orderedColumns at: r) + 1.
  1124.         start to: self class cardsPerSuit do: [:c |
  1125.             | index thisCard |
  1126.  
  1127.             "Get an integral random number in the range [1, cards size]."
  1128.             index := (self random * (cards size-1)) rounded + 1.  
  1129.  
  1130.             thisCard := cards removeAtIndex: index.
  1131.             self cardAt: r @ c put: thisCard.
  1132.         ].
  1133.     ].
  1134.     self removeAces.!
  1135.  
  1136. undefinedLastMove
  1137.     "Prevent the player from performing an undo last move
  1138.     command.  This is necessary at the start of a game and 
  1139.     immediately after a shuffle command."
  1140.  
  1141.     self lastMove: nil.! !
  1142.  
  1143. !Montana methodsFor: 'statistics'!
  1144.  
  1145. collectStatistics: count
  1146.     | results log tock tick ticker moves max |
  1147.  
  1148.     results := Array new: self class numberOfCards.
  1149.     moves := Dictionary new.
  1150.     results atAllPut: 0.
  1151.     tock := (count // 100) rounded.
  1152.     tick := tock.
  1153.     ticker := 0.
  1154.  
  1155.     count timesRepeat: [
  1156.         | placed remaining used |
  1157.  
  1158.         self automaticPlay.
  1159.         placed := self numberOfCardsInOrder.
  1160.         remaining := self class placeableCards - placed + 1.
  1161.         results at: remaining put: (results at: remaining) + 1.
  1162.         used := moves at: self moveCounter ifAbsent: [0].
  1163.         moves at: self moveCounter put: used+1.
  1164.         self newGame.
  1165.         tick := tick - 1.
  1166.         tick = 0 ifTrue: [
  1167.             ticker := ticker + 1.
  1168.             Transcript show: ticker printString, '% at ', Time now printString; cr.
  1169.             tick := tock.
  1170.         ].
  1171.     ].
  1172.  
  1173.     log := 'Remaining.results' asFilename writeStream.
  1174.     log nextPutAll: 'Remaining'; tab.
  1175.     log nextPutAll: 'Count'; cr.
  1176.  
  1177.     1 to: results size do: [:k |
  1178.         log nextPutAll: (k-1) printString.
  1179.         log tab.
  1180.         log nextPutAll: (results at: k) printString.
  1181.         log cr.
  1182.     ].
  1183.     log close.
  1184.  
  1185.     log := 'Moves.results' asFilename writeStream.
  1186.     log nextPutAll: 'Moves'; tab.
  1187.     log nextPutAll: 'Count'; cr.
  1188.     max := moves keys inject: 0 into: [:big :k | big max: k].
  1189.  
  1190.     1 to: max do: [:k |
  1191.         log nextPutAll: k printString.
  1192.         log tab.
  1193.         log nextPutAll: (moves at: k ifAbsent: [0]) printString.
  1194.         log cr.
  1195.     ].
  1196.     log close.! !
  1197.  
  1198. !Montana methodsFor: 'testing'!
  1199.  
  1200. hasLastMove
  1201.     "Do we have a valid last move that can be undone?"
  1202.  
  1203.     ^self lastMove notNil!
  1204.  
  1205. isCardAt: index
  1206.     "Is there a card at the given location?"
  1207.  
  1208.     ^(self cardAt: index) notNil! !
  1209.  
  1210. !Montana methodsFor: 'view adaptor'!
  1211.  
  1212. instructions
  1213.     | title info |
  1214.  
  1215.     title := Text string: 'Montana Instructions' emphasis: 5.
  1216.     info := Text string: '
  1217.  
  1218. Montana is a solitaire card game where the player tries to order cards
  1219. by suit from 2 to king.  A new game starts with all the cards dealt at
  1220. random in four rows of thirteen columns.   Then all the aces are removed
  1221. to leave four gaps.
  1222.  
  1223. Cards can only be moved into the gaps.   A card can only be moved to 
  1224. the right of the card of the same suit but with face value one lower.
  1225. For example, if there is a gap to the right of the 3 of spades then
  1226. the only card that can be moved there is the 4 of spades.   Any gap
  1227. to the right of a king is dead since no card has higher value and so
  1228. nothing can be moved there.   If there is a gap in the leftmost column
  1229. then any 2 may be moved there.  To move a card, simply click on it using
  1230. the left mouse button.   If that card cannot move then the card it should 
  1231. go behind is highlighted.   Clicking on a gap highlights the card that can be 
  1232. moved to that gap.   An illegal action causes the board to flash.
  1233.  
  1234. Once there are no more mores available, you can shuffle all the cards
  1235. that are no in the correct order.   You are allowed two shuffles per
  1236. game.   Commands to shuffle the cards, start a new game, and undo the
  1237. last move are selected from a pop-up menu on the middle mouse button.
  1238.  
  1239. The status window below the board tells you how many cards
  1240. are in order and the the number of shuffles remaining.
  1241.  
  1242. (c)  Kevin Waite, 1990.' emphasis: 1.
  1243.  
  1244.     ^title, info!
  1245.  
  1246. scoreText
  1247.     "Returns a text string that is used in displaying the score."
  1248.  
  1249.     | comment remaining percentage placed |
  1250.  
  1251.     placed := self numberOfCardsInOrder.
  1252.     remaining := self class placeableCards - placed.
  1253.     comment := 'Cards still to position = ', remaining printString.
  1254.     percentage := ((placed / self class placeableCards) * 100) rounded.
  1255.     comment := comment, '   Score = ', percentage printString, '%.   '.
  1256.     comment := comment, '   Number of moves = ', self moveCounter printString, '    '.
  1257.     ^Text string: comment withCRs emphasis: 2!
  1258.  
  1259. shuffleText
  1260.     "Return a text string saying how many shuffles are
  1261.     left in the game."
  1262.  
  1263.     | comment |
  1264.  
  1265.     comment := 'Remaining shuffles = ', self shufflesLeft printString.
  1266.     ^Text string: comment emphasis: 2!
  1267.  
  1268. status
  1269.     "The complete status message for the game is a
  1270.     concatenation of the score and the remaining shuffles."
  1271.  
  1272.     ^self scoreText, self shuffleText! !
  1273.  
  1274. !Montana methodsFor: 'menu functions'!
  1275.  
  1276. openInstructions
  1277.     | topView infoView |
  1278.  
  1279.     topView := StandardSystemView new model: self.
  1280.     topView borderWidth: 2.
  1281.     topView label: 'Montana Instructions'.
  1282.  
  1283.     infoView := TextView on: self aspect: #instructions change: nil menu: nil.
  1284.     infoView borderWidth: 1.
  1285.     topView addSubView: infoView.
  1286.  
  1287.     topView minimumSize: 500@500.
  1288.     topView controller open.!
  1289.  
  1290. shuffle
  1291.     "Randomly re-arrange those cards that 
  1292.     are not in correct order."
  1293.  
  1294.     |  ordered cards aces |
  1295.  
  1296.     self shufflesLeft = 0 ifTrue: [^self "Cannot shuffle any more."].
  1297.     aces := ReadStream on: self class aces.
  1298.     cards := OrderedCollection new: self class numberOfCards.
  1299.  
  1300.     "Find where the ordered part of each row ends and collect
  1301.     together all those cards that appear in the unordered part."
  1302.  
  1303.     ordered := Array new: self class numberOfSuits.
  1304.     1 to: self class numberOfSuits do: [:r |
  1305.         | count |
  1306.  
  1307.         count := self numberOfPositionedCardsInRow: r.
  1308.         ordered at: r put: count.
  1309.  
  1310.         (count+1) to: self class cardsPerSuit do: [:c |
  1311.             | thisCard |
  1312.  
  1313.             thisCard := (self isCardAt: r @ c)
  1314.                             ifTrue: [self cardAt: r @ c]
  1315.                             ifFalse: [aces next].
  1316.  
  1317.             cards add: thisCard.
  1318.         ].
  1319.     ].
  1320.  
  1321.     self shuffleCards: cards ordered: ordered.
  1322.     self changed: #game.
  1323.     self oneLessShuffleLeft.
  1324.     self undefinedLastMove.!
  1325.  
  1326. undoLastMove
  1327.     "The last move made by the player is reversed with the
  1328.     property that two consecutive undo operations leave the
  1329.     board untouched.
  1330.     The move information is held as an array of two points.
  1331.     Note that undoing the last move DOES increment the
  1332.     move counter."
  1333.  
  1334.     | start finish |
  1335.  
  1336.     start := self lastMove first.
  1337.     finish := self lastMove last.
  1338.     self moveCardAt: finish to: start.! !
  1339. "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
  1340.  
  1341. Montana class
  1342.     instanceVariableNames: ''!
  1343.  
  1344.  
  1345. !Montana class methodsFor: 'constants'!
  1346.  
  1347. aces
  1348.     ^Array
  1349.         with: Club ace
  1350.         with: Diamond ace
  1351.         with: Spade ace
  1352.         with: Heart ace!
  1353.  
  1354. cardsPerSuit
  1355.     "Return the number of cards in each suit."
  1356.  
  1357.     ^13!
  1358.  
  1359. numberOfCards
  1360.     "Return the total number of cards in the pack."
  1361.  
  1362.     ^52!
  1363.  
  1364. numberOfSuits
  1365.     "Return the number of suits."
  1366.  
  1367.     ^4!
  1368.  
  1369. placeableCards
  1370.     "How many cards does the user have to arrange to 
  1371.     complete the game?"
  1372.  
  1373.     ^self numberOfCards - 4 "Number of aces"!
  1374.  
  1375. statusHeight
  1376.     "What is the height of the status panel of the game?"
  1377.  
  1378.     ^40! !
  1379.  
  1380. !Montana class methodsFor: 'initialize-release'!
  1381.  
  1382. initialize
  1383.     "Reset the random number generator."
  1384.  
  1385.     RND := Random new.! !
  1386.  
  1387. !Montana class methodsFor: 'instance creation'!
  1388.  
  1389. instructions
  1390.     "Provide some help information on how to play the game."
  1391.  
  1392.     self new openInstructions!
  1393.  
  1394. new
  1395.     self initialize.
  1396.     ^super new initialize!
  1397.  
  1398. open
  1399.     "Create a new instance of the game and open a graphical
  1400.     display of the board and a textual summary of the game status."
  1401.  
  1402.     "Montana open"
  1403.  
  1404.     | montana topView montanaView boardSize statusView size |
  1405.  
  1406.     montana := self new newGame.
  1407.     topView := StandardSystemView new model: montana.
  1408.     topView borderWidth: 2.
  1409.     topView label: 'Montana'.
  1410.  
  1411.     montanaView := MontanaView new model: montana.
  1412.     topView addSubView: montanaView.
  1413.  
  1414.     statusView := TextView on: montana aspect: #status change: nil menu: nil.
  1415.  
  1416.     topView addSubView: montanaView in: (0@0 extent: 1@0.8) borderWidth: 2.
  1417.     topView addSubView: statusView in: (0@0.8 extent: 1@0.2) borderWidth: 2.
  1418.  
  1419.      boardSize := montanaView defaultWindow extent.
  1420.     size := boardSize + (0 @ self statusHeight).
  1421.     topView minimumSize: size.
  1422.     topView maximumSize: size.
  1423.  
  1424.     topView controller open.! !
  1425.  
  1426. Montana initialize!
  1427.  
  1428.  
  1429. MouseMenuController subclass: #MontanaController
  1430.     instanceVariableNames: ''
  1431.     classVariableNames: 'Game GameAndUndo GameMenu Shuffle ShuffleAndMove ShuffleAndUndo ShuffleMenu '
  1432.     poolDictionaries: ''
  1433.     category: 'Montana'!
  1434. MontanaController comment:
  1435. 'My instances control the user interaction in a game of Montana.
  1436. The  red mouse button is used to control the movement of the cards.
  1437. To move a card, simply click on it using the left mouse button. If that 
  1438. card cannot move then the card it should go behind is highlighted. 
  1439. Clicking on a gap highlights the card that can be moved to that gap. 
  1440. An illegal action causes the board to flash. 
  1441.  
  1442. Commands to shuffle the cards, start a new game, and undo the
  1443. last move are selected from a pop-up menu on the middle mouse button.'!
  1444.  
  1445.  
  1446. !MontanaController methodsFor: 'control activity'!
  1447.  
  1448. isControlActive
  1449.     ^super isControlActive & sensor blueButtonPressed not! !
  1450.  
  1451. !MontanaController methodsFor: 'menus'!
  1452.  
  1453. gameMenu
  1454.     ^self model hasLastMove
  1455.         ifTrue: [GameAndUndo]
  1456.         ifFalse: [Game]!
  1457.  
  1458. menu
  1459.     "Return an ActionMenu offering the commands that
  1460.     are applicable to this state of the game."
  1461.  
  1462.     ^self model shufflesLeft > 0 
  1463.         ifTrue: [self shuffleMenu]
  1464.         ifFalse: [self gameMenu]!
  1465.  
  1466. shuffleMenu
  1467.     ^self model hasLastMove
  1468.         ifTrue: [ShuffleAndUndo]
  1469.         ifFalse: [Shuffle]! !
  1470.  
  1471. !MontanaController methodsFor: 'menu functions'!
  1472.  
  1473. moveCardAtPosition: position 
  1474.     "Move the card at the specified position. See my class 
  1475.     comment for details of what happens when an attempt 
  1476.     is made to move a card."
  1477.  
  1478.     | destination doAFullRun |
  1479.  
  1480.     doAFullRun := Sensor leftShiftDown.
  1481.     doAFullRun ifTrue: [ |success |
  1482.         success := self model placeOrderedCardAt: position.
  1483.         success ifFalse: [self view flash].
  1484.         ^self
  1485.     ].
  1486.  
  1487.     (self model isCardAt: position)
  1488.         ifFalse: [^self view showDescendentOfCardAt: position].
  1489.  
  1490.     destination := self model destinationForCardAt: position.
  1491.     destination isNil ifTrue: [^self view showAncestorOfCardAt: position].
  1492.  
  1493.     self model moveCardAtPosition: position.! !
  1494.  
  1495. !MontanaController methodsFor: 'mouse activity'!
  1496.  
  1497. mousePositionAsBoardLocation
  1498.     "Convert the current mouse position into a Point that
  1499.     describes the position of the mouse on the Montana
  1500.     board.  The x position refers to the row number;  the
  1501.     y value gives the column number."
  1502.  
  1503.     | origin locus cardSize offset increment count row col |
  1504.  
  1505.     origin := self view insetDisplayBox origin.
  1506.     locus := sensor mousePoint - origin.
  1507.     cardSize := PlayingCard extent.
  1508.     offset := 5.
  1509.      
  1510.     increment := cardSize y + offset.
  1511.     count := (locus y \\ increment) - offset.
  1512.     row := count >= 0 
  1513.                 ifTrue: [(locus y // increment) + 1]
  1514.                 ifFalse: [^nil].
  1515.  
  1516.     increment := cardSize x + offset.
  1517.     count := (locus x \\ increment) - offset.
  1518.     col := count >= 0 
  1519.                 ifTrue: [(locus x // increment) + 1]
  1520.                 ifFalse: [^nil].
  1521.  
  1522.     ^row @ col!
  1523.  
  1524. redButtonActivity
  1525.     | locus |    
  1526.  
  1527.     locus := self mousePositionAsBoardLocation.
  1528.     locus isNil ifFalse: [self moveCardAtPosition: locus].!
  1529.  
  1530. yellowButtonActivity
  1531.     | aMenu index selector saved  |
  1532.  
  1533.     sensor leftShiftDown ifTrue: [
  1534.         self model priorityMove.
  1535.         sensor waitNoButton.
  1536.         ^self
  1537.     ].
  1538.  
  1539.     aMenu := self menu.
  1540.     aMenu isNil ifFalse: [
  1541.         self controlTerminate.
  1542.         index :=  aMenu startUp.
  1543.         index ~= 0 ifTrue: [
  1544.             selector := aMenu selectorAt: index. 
  1545.             saved := Cursor currentCursor.
  1546.             Cursor currentCursor: Cursor execute.
  1547.             self model perform: selector.
  1548.             Cursor currentCursor: saved.
  1549.         ].
  1550.         self controlInitialize.
  1551.     ].! !
  1552. "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
  1553.  
  1554. MontanaController class
  1555.     instanceVariableNames: ''!
  1556.  
  1557.  
  1558. !MontanaController class methodsFor: 'initialize-release'!
  1559.  
  1560. initialize
  1561.     "MontanaController initialize."
  1562.  
  1563.     Game := ActionMenu
  1564.                     labels: 'Start New Game\Instructions' withCRs
  1565.                     lines: #(1)
  1566.                     selectors: #(newGame openInstructions).    
  1567.  
  1568.     GameAndUndo := ActionMenu
  1569.                     labels: 'Start New Game\Undo Last Move\Heuristic Play\Instructions' withCRs
  1570.                     lines: #(1)
  1571.                     selectors: #(newGame undoLastMove automaticPlay openInstructions).    
  1572.  
  1573.     Shuffle := ActionMenu
  1574.                     labels: 'Start New Game\Shuffle Unordered Cards\Heuristic Play\Instructions' withCRs
  1575.                     lines: #()
  1576.                     selectors: #(newGame shuffle automaticPlay openInstructions).    
  1577.  
  1578.     ShuffleAndUndo := ActionMenu
  1579.                     labels: 'Start New Game\Shuffle Unordered Cards\Undo Last Move\Heuristic Play\Instructions' withCRs
  1580.                     lines: #()
  1581.                     selectors: #(newGame shuffle undoLastMove automaticPlay openInstructions).! !
  1582.  
  1583. MontanaController initialize!
  1584.  
  1585.  
  1586.  
  1587.  
  1588.